package com.gitee.feizns.webtemplate.commons.advice;

import com.gitee.feizns.webtemplate.commons.annotation.PackAbsoluteUrl;
import com.gitee.feizns.webtemplate.commons.utils.RequestUtils;
import com.gitee.feizns.webtemplate.commons.utils.reflect.FieldUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @author feizns
 * @since 2019/4/8
 */
@RestControllerAdvice
public class PackUrlResponseBodyAdvice implements ResponseBodyAdvice {

    /**
     * 忽略的类型
     */
    private static final Set<Class<?>> IGNORE_TYPES = new HashSet<>();

    static {
        IGNORE_TYPES.add(Byte.class);
        IGNORE_TYPES.add(Short.class);
        IGNORE_TYPES.add(Integer.class);
        IGNORE_TYPES.add(Long.class);
        IGNORE_TYPES.add(Float.class);
        IGNORE_TYPES.add(Double.class);
        IGNORE_TYPES.add(Boolean.class);
        IGNORE_TYPES.add(Character.class);
        IGNORE_TYPES.add(String.class);
        IGNORE_TYPES.add(Date.class);
        IGNORE_TYPES.add(Class.class);
    }

    /**
     * 自定义路径前缀
     */
    private String urlPrefix;


    public PackUrlResponseBodyAdvice() { }

    public PackUrlResponseBodyAdvice(String urlPrefix) {
        this.urlPrefix = urlPrefix;
    }

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }


    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {

        packUrl(body);

        return body;
    }

    /**
     *
     * @param obj
     */
    private void packUrl(Object obj) {

        if ( obj == null ) return;

        Class<?> objClass = obj.getClass();
        PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(objClass);

        //遍历属性
        for (PropertyDescriptor descriptor : descriptors) {
            Method readMethod = descriptor.getReadMethod();
            Method writeMethod = descriptor.getWriteMethod();

            //是一个可读的属性
            if ( readMethod != null ) {
                try {

                    Object originalResult = readMethod.invoke(obj);

                    if ( originalResult != null ) {

                        //获取字段真实类型
                        Class<?> fieldClass = originalResult.getClass();

                        Field field = FieldUtils.getField(objClass, descriptor.getName());

                        if ( field != null && field.isAnnotationPresent(PackAbsoluteUrl.class) && writeMethod != null ) {
                            writeMethod.invoke(obj, getUrlPrefix() + originalResult);

                        } else if ( fieldClass.isArray() ) {        //array
                            for (int i = 0, size = Array.getLength(originalResult); i < size; i++) {
                                packUrl(Array.get(originalResult, i));
                            }

                        } else if ( Iterable.class.isAssignableFrom(fieldClass) ) { //collection
                            ((Iterable) originalResult).forEach(this::packUrl);

                        } else if ( Map.class.isAssignableFrom(fieldClass) ) {      //map
                            ((Map) originalResult).forEach((key, val) -> this.packUrl(val));

                        } else if ( fieldClass.isPrimitive() == false && IGNORE_TYPES.contains(fieldClass) == false ) {
                            packUrl(originalResult);
                        }

                    }

                } catch (ReflectiveOperationException e) {
                    //忽略当前字段的处理错误。
                }
            }
        }

    }

    /**
     * 获取前缀
     * @return
     */
    private String getUrlPrefix() {
        return urlPrefix != null
                ? urlPrefix
                : RequestUtils.getBasePath();
    }

}
